package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.learning.constants.LearningConstants.POINTS_BOARD_TABLE_PREFIX;
import static com.tianji.learning.constants.RedisConstants.POINTS_BOARD_KEY_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author xioayang
 * @since 2024-06-30
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {
        private final StringRedisTemplate redisTemplate;
        private final UserClient userClient;
    /**
     * 分页查询积分排行榜
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO pageQueryPointsBoard(PointsBoardQuery query) {
        //1.判断是否是当前赛季
        Long season = query.getSeason();
        boolean isCurrentSeason = ObjectUtil.isEmpty(season) || season == 0;

        // 2、查询我的积分信息
        String key = POINTS_BOARD_KEY_PREFIX + LocalDate.now().format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        PointsBoard myBoard = isCurrentSeason ? queryMyCurrentBoard(key) : queryMyHistoryBoard(season);

        // 3、查询指定赛季的榜单列表
        List<PointsBoard> boards = isCurrentSeason ? queryCurrentBoardList(key,query.getPageNo(),query.getPageSize())
                : queryHistoryBoardList(query);

        // 4、封装Vo
        // 4.1、处理我的数据
        PointsBoardVO vo = new PointsBoardVO();
        vo.setPoints(myBoard.getPoints());
        vo.setRank(myBoard.getRank());

        // 4.2、处理榜单数据
        if(ObjectUtil.isEmpty(boards)){
            return vo;
        }

        // 4.2.1、查询用户信息
        Set<Long> userIds = boards.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userInfoList = userClient.queryUserByIds(userIds);
        Map<Long, String> userInfoMap = new HashMap<>(userIds.size());
        if(ObjectUtil.isNotEmpty(userInfoList)){
            userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        }

        // 4.2.2、循环组装ItemVo List
        List<PointsBoardItemVO> itemVoList = new ArrayList<>(boards.size());
        for (PointsBoard board : boards) {
            PointsBoardItemVO itemVO = new PointsBoardItemVO();

            itemVO.setPoints(board.getPoints());
            itemVO.setRank(board.getRank());
            itemVO.setName(userInfoMap.getOrDefault(board.getUserId(),""));

            itemVoList.add(itemVO);
        }
        vo.setBoardList(itemVoList);
        // 5、返回VO
        return vo;
    }

    /**
     * 根据赛季id创建积分排行榜
     *
     * @param seasonId
     */
    @Override
    public void createPointsBoardTableBySeasonId(Integer seasonId) {
        this.getBaseMapper().createPointsBoardTable(POINTS_BOARD_TABLE_PREFIX + seasonId);
    }

    /**
     * 查询我的当前赛季积分情况
     * @param key redis zest key
     * @return
     */
    private PointsBoard queryMyCurrentBoard(String key) {
        //1、绑定key
        BoundZSetOperations<String, String> boundZSetOps = redisTemplate.boundZSetOps(key);

        // 2、获取当前登录人
        String userId = UserContext.getUser().toString();

        // 3、查询积分
        Double points = boundZSetOps.score(userId);

        // 4、查询排名
        Long rank = boundZSetOps.rank(userId);

        //第二种获取积分排名方法
        //Double score = redisTemplate.opsForZSet().score(key, userId);
        //Long rank = redisTemplate.opsForZSet().reverseRank(key, userId);

        // 5、封装返回
        PointsBoard pointsBoard = new PointsBoard();
        pointsBoard.setPoints(points == null ? 0 : points.intValue());
        pointsBoard.setRank(rank == null ? 0 : rank.intValue());
        return pointsBoard;
    }

    /**
     * 查询我的历史赛季积分情况 TODO
     * @param season 赛季ID
     * @return
     */
    private PointsBoard queryMyHistoryBoard(Long season) {
        return null;
    }

    /**
     * 查询当前赛季积分榜单列表
     * @param key
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
        // 1、分页查询（第1页： 0 - 9   第2页：10 - 19）
        int from = (pageNo - 1) * pageSize;
        int end = from + pageSize - 1;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, from, end);
        if (ObjectUtil.isEmpty(typedTuples)){
            return CollUtils.emptyList();
        }
        // 2、封装List返回
        int rank =from + 1;
        List<PointsBoard> list = new ArrayList<>(typedTuples.size());
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            String userId = typedTuple.getValue();
            Double points = typedTuple.getScore();
            if (ObjectUtil.isEmpty(userId)|| ObjectUtil.isEmpty(points)){
                continue;
            }
            PointsBoard pointsBoard = new PointsBoard();
            pointsBoard.setPoints(points.intValue());
            pointsBoard.setUserId(Long.valueOf(userId));
            pointsBoard.setRank(rank++);
            list.add(pointsBoard);
        }
        return list;
    }

    /**
     * 查询历史赛季积分榜单列表  TODO
     * @param query
     * @return
     */
    private List<PointsBoard> queryHistoryBoardList(PointsBoardQuery query) {
        return null;
    }
}
